package com.situ.mall.common.util;

import java.net.InetAddress;
import java.util.concurrent.ThreadLocalRandom;

public class XueHuaUtil {
    public static class OrderNumberGenerator {
        private static final long START_TIMESTAMP = 1631980800000L; // 2021-09-18 00:00:00
        private static final long WORKER_ID_BITS = 10L;
        private static final long SEQUENCE_BITS = 12L;

        private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
        private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

        private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
        private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

        private static long workerId;
        private static long sequence = 0L;
        private static long lastTimestamp = -1L;

        static {
            try {
                byte[] ipAddress = InetAddress.getLocalHost().getAddress();
                workerId = (long) (ipAddress[ipAddress.length - 1] & 0xFF);
            } catch (Exception e) {
                workerId = ThreadLocalRandom.current().nextLong(0, MAX_WORKER_ID + 1);
            }
            workerId = workerId & MAX_WORKER_ID;
        }

        public static synchronized long generateOrderNumber() {
            long currentTimestamp = System.currentTimeMillis();

            if (currentTimestamp < lastTimestamp) {
                throw new IllegalStateException("Invalid system clock");
            }

            if (currentTimestamp == lastTimestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                if (sequence == 0) {
                    currentTimestamp = nextTimestamp();
                }
            } else {
                sequence = 0;
            }

            lastTimestamp = currentTimestamp;

            return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
                    | (workerId << WORKER_ID_SHIFT)
                    | sequence;
        }

        private static long nextTimestamp() {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) {
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }

    }
}
